package be.ipl.finito.domaine;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.MapKeyJoinColumn;
import javax.persistence.Table;
import javax.persistence.Transient;

import be.ipl.finito.util.CaseGrille;
import be.ipl.finito.util.GrilleUtil;
import be.ipl.finito.util.Util;

@SuppressWarnings("serial")
@Entity
@Table(schema = "FINITO", name = "GRILLES")
public class Grille implements Serializable {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int id;

	// garde chaque jeton et son index_case
	@ElementCollection
	@CollectionTable(schema = "FINITO", name = "CASES_GRILLES")
	@Column(name = "INDEX")
	@MapKeyJoinColumn(name = "ID_JETON")
	private Map<Jeton, Integer> casesGrilles = new HashMap<Jeton, Integer>(
			Partie.NBR_JETONS);

	@ManyToOne(fetch = FetchType.EAGER)
	private Joueur proprietaire;// définit le joueur qui a la grille

	@ManyToOne(fetch = FetchType.EAGER)
	private Partie partie;

	@Transient
	private Map<Integer, Jeton> jetonsReserve = new HashMap<Integer, Jeton>(
			Partie.MAX_JOUEURS);
	public static final int NBR_CASES = 20;

	protected Grille() {
		super();
	}

	public Grille(Joueur proprietaire, Partie partie) {
		Util.checkObject(proprietaire);
		Util.checkObject(partie);
		this.proprietaire = proprietaire;
		this.partie = partie;
	}

	public Joueur getProprietaire() {
		return this.proprietaire;
	}

	public Partie getPartie() {
		return this.partie;
	}

	public int getId() {
		return this.id;
	}

	public List<CaseGrille> getCaseOccupees() {

		List<Jeton> key = new ArrayList<>(this.casesGrilles.keySet());
		List<CaseGrille> caseOcupees = new ArrayList<CaseGrille>();
		for (Jeton j : key) {
			caseOcupees.add(new CaseGrille(j, this.casesGrilles.get(j)));
		}
		return caseOcupees;
	}

	public List<Jeton> getJetonsJouables() {
		return new ArrayList<>(this.jetonsReserve.values());
	}

	public boolean ajouterJetonReserve(Jeton jeton) {
		Util.checkObject(jeton);
		if (this.jetonsReserve.size() == Partie.MAX_JETON_TOUR1) {
			return false;
		}

		this.jetonsReserve.put(jeton.getId(), jeton);
		return true;
	}

	public boolean retiterJetonReserve(Jeton jeton) {
		Util.checkObject(jeton);
		if (this.jetonsReserve.containsKey(jeton.getId())) {
			this.jetonsReserve.remove(jeton.getId());
			return true;
		}
		return false;
	}

	// Détermine dans quelle case les joueurs vont placer le jeton
	int positionJetons(Partie partie) {
		return partie.getRandom().getEntierEntreMinMAX(0, NBR_CASES);
	}

	public boolean mettreJeton(Jeton jeton, int indexCaseCliquee, int valeurDe) {
		boolean plusPetit = true;
		int indexDe = GrilleUtil.getIndexDuNumeroGauche(valeurDe);
		if (indexCaseCliquee > indexDe) {
			plusPetit = false;
		}
		int valeurCase = GrilleUtil.getCaseNumber(indexCaseCliquee);
		// si clic sur une case du valeur du dé vide, on ajoute
		if (valeurCase == valeurDe
				&& !this.casesGrilles.containsValue(indexCaseCliquee)) {
			new CaseGrille(jeton, indexCaseCliquee);
			this.casesGrilles.put(jeton, indexCaseCliquee);
			return true;
		}
		// si clic sur une case différente de la valeur du dé
		// On verifie si les cases à valeur du dé sont prise

		if (indexDe == 1 || indexDe == 2 || indexDe == 35 || indexDe == 36) {
			if (this.casesGrilles.get(indexDe) == null) {
				return false;
			}
		} else {
			if (this.casesGrilles.get(indexDe) == null
					|| this.casesGrilles.get(indexDe + 1) == null) {
				return false;
			}
		}
		if (plusPetit) {
			for (int i = indexCaseCliquee; i <= indexDe; i++) {
				if (this.casesGrilles.get(i) == null) {
					return false;
				}
			}
			new CaseGrille(jeton, indexCaseCliquee);
			this.casesGrilles.put(jeton, indexCaseCliquee);
			return true;
		} else {
			for (int i = indexDe; i <= indexCaseCliquee; i++) {
				if (this.casesGrilles.get(i) == null) {
					return false;
				}
			}
			new CaseGrille(jeton, indexCaseCliquee);
			this.casesGrilles.put(jeton, indexCaseCliquee);
			return true;
		}
	}
	
	public boolean mettreJetonV2(Jeton jeton, int indexCaseCliquee, int valeurDe, int valeurCase) {
		boolean plusPetit = true;
		int indexDe = GrilleUtil.getIndexDuNumeroGauche(valeurDe);
		if (indexCaseCliquee > indexDe) {
			plusPetit = false;
		}
		// si clic sur une case du valeur du dé vide, on ajoute
		if (valeurCase == valeurDe
				&& !this.casesGrilles.containsValue(indexCaseCliquee)) {
			this.casesGrilles.put(jeton, indexCaseCliquee);
			return true;
		}
		// si clic sur une case différente de la valeur du dé
		// On verifie si les cases à valeur du dé sont prise

		if (indexDe == 1 || indexDe == 2 || indexDe == 35 || indexDe == 36) {
			if (this.casesGrilles.get(indexDe) == null) {
				return false;
			}
		} else {
			if (this.casesGrilles.get(indexDe) == null
					|| this.casesGrilles.get(indexDe + 1) == null) {
				return false;
			}
		}
		if (plusPetit) {
			for (int i = indexCaseCliquee; i <= indexDe; i++) {
				if (this.casesGrilles.get(i) == null) {
					return false;
				}
			}
			this.casesGrilles.put(jeton, indexCaseCliquee);
			return true;
		} else {
			for (int i = indexDe; i <= indexCaseCliquee; i++) {
				if (this.casesGrilles.get(i) == null) {
					return false;
				}
			}
			this.casesGrilles.put(jeton, indexCaseCliquee);
			return true;
		}
	}

	public boolean deplacerJeton(CaseGrille ancienne, int nouvelle, int valeurDe) {
		if (this.mettreJeton(ancienne.getJeton(), nouvelle, valeurDe)) {
			this.casesGrilles.remove(ancienne.getIndex());
			return true;
		}
		return false;
	}

}